﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using Nancy;
using Nancy.Responses.Negotiation;
using QQ2564874169.Core;
using QQ2564874169.WebFx.Nancy.Responses;

namespace QQ2564874169.WebFx.Nancy.Web
{
    public abstract class NancyWebApi : NancyController
    {
        private static readonly Regex _isnum = new Regex("_(?<num>\\d+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
        public static string QueryParamNameByVer { get; set; }

        static NancyWebApi()
        {
            QueryParamNameByVer = "ver";
        }

        protected override string OnPath(string path)
        {
            var items = path.TrimStart('/').Split('/').ToArray();
            if (items.Length > 1)
            {
                const string repstr = "api";
                var ctrl = items[0];
                if (ctrl.EndsWith(repstr, StringComparison.OrdinalIgnoreCase))
                    items[0] = ctrl.Substring(0, ctrl.Length - repstr.Length);
            }
            return "/" + string.Join("/", items);
        }

        protected override RouteSetting[] LoadAction(Type controllerType)
        {
            var settings = base.LoadAction(controllerType);
            var apis = new List<ApiRouteSetting>();

            foreach (var route in settings)
            {
                long? num = null;
                var match = _isnum.Match(route.Action.Name);
                if (match.Success)
                {
                    long outl;
                    if (long.TryParse(match.Groups["num"].Value, out outl))
                    {
                        num = outl;
                    }
                }
                num = num.GetValueOrDefault(-1);

                foreach (var path in route.Routes)
                {
                    var sett = apis.FirstOrDefault(i => i.Path.QQEquals(path) && i.Method == route.Method);
                    if (sett == null)
                    {
                        sett = new ApiRouteSetting(route.Method, new[] { path });
                        apis.Add(sett);
                    }
                    sett.VerApis.Add(new ExecApi
                    {
                        Ver = num.Value,
                        Action = route.Action
                    });
                    sett.Action = num < 0 ? route.Action : sett.VerApis.OrderByDescending(i => i.Ver).First().Action;
                }
            }
            return apis.Cast<RouteSetting>().ToArray();
        }

        protected override Negotiator GetView(string path, object model)
        {
            throw new NotSupportedException();
        }

        protected virtual JsonResponse Result(object data, Encoding encoding = null)
        {
            return new JsonResponse(data, encoding);
        }

        public class ExecApi
        {
            public long Ver { get; internal set; }
            public MethodInfo Action { get; internal set; }
        }

        public class ApiRouteSetting : RouteSetting
        {
            public List<ExecApi> VerApis { get; }

            public string Path { get; }

            public ApiRouteSetting(RequestMethod type, string[] routes) : base(type, routes)
            {
                VerApis = new List<ExecApi>();
                Path = routes.First();
            }

            public override MethodInfo GetAction(NancyContext context)
            {
                string ver = context.Request.Query[QueryParamNameByVer];
                long num;
                if (string.IsNullOrEmpty(ver) || long.TryParse(ver, out num) == false)
                {
                    num = -1;
                }
                if (num < 0)
                {
                    var defapi = VerApis.FirstOrDefault(i => i.Ver < 0);
                    if (defapi != null)
                    {
                        return defapi.Action;
                    }
                    return VerApis.OrderByDescending(i => i.Ver).First().Action;
                }
                var api = VerApis.Where(i => i.Ver <= num).OrderByDescending(i => i.Ver).FirstOrDefault();
                return api != null ? api.Action : Action;
            }
        }
    }
}
